/*
 * $Id: PDFDocument.java,v 1.4 2007/09/22 12:58:40 gil1 Exp $ $Date: 2007/09/22
 * 12:58:40 $ This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version. This library is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU Lesser General Public License for more details. You should have
 * received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package pdf.handler;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Vector;

/**
 * <p>
 * This class is the base of the PDF generator. A PDFDocument class is created
 * for a document, and each page, object, annotation, etc is added to the
 * document. Once complete, the document can be written to an OutputStream, and
 * the PDF document's internal structures are kept in sync.
 * </p>
 * <p>
 * Note that most programmers using this package will NEVER access one of these
 * objects directly. Most everything can be done using <code>PDFJob</code> and
 * <code>PDFGraphics</code>, so you don't need to directly instantiate a
 * <code>PDFDocument</code>
 * </p>
 * <p>
 * ezb - 20011115 - Wondering if the constructors should even be public. When
 * would someone want to make one of these and manipulate it outside the context
 * of a job and graphics object?
 * </p>
 * 
 * @author Peter T Mount, http://www.retep.org.uk/pdf/
 * @author Eric Z. Beard, ericzbeard@hotmail.com
 * @author Gilbert DeLeeuw, gil1@users.sourceforge.net
 * @version $Revision: 1.4 $, $Date: 2007/09/22 12:58:40 $
 */
public class PDFDocument implements Serializable
{

   /*
    * NOTE: This class originated in uk.org.retep.pdf, by Peter T. Mount, and
    * it has been modified by Eric Z. Beard, ericzbeard@hotmail.com. The
    * package name was changed to pdf.handler and several inner classes were moved
    * out into their own files.
    */

   /**
    * <p>
    * This page mode indicates that when the document is opened, it is
    * displayed in full-screen-mode. There is no menu bar, window controls nor
    * any other window present.
    * </p>
    */
   public static final int FULLSCREEN = 3;

   /**
    * <p>
    * These map the page modes just defined to the pagemodes setting of PDF.
    * </p>
    */
   public static final String PDF_PAGE_MODES[] = {
      "/UseNone",
      "/UseOutlines",
      "/UseThumbs",
      "/FullScreen"
   };

   /**
    * 
    */
   private static final long serialVersionUID = 1L;

   /**
    * <p>
    * This page mode indicates that the document should be opened just with the
    * page visible. This is the default
    * </p>
    */
   public static final int USENONE = 0;

   /**
    * <p>
    * This page mode indicates that the Outlines should also be displayed when
    * the document is opened.
    * </p>
    */
   public static final int USEOUTLINES = 1;

   /**
    * <p>
    * This page mode indicates that the Thumbnails should be visible when the
    * document first opens.
    * </p>
    */
   public static final int USETHUMBS = 2;

   /**
    * This is the Catalog object, which is required by each PDF Document
    */
   private PDFCatalog catalog;

   /**
    * This holds a PDFObject describing the default border for annotations.
    * It's only used when the document is being written.
    */
   protected PDFObject defaultOutlineBorder;

   /**
    * This is used to provide a unique name for a font
    */
   private int fontid = 0;

   /**
    * This holds the current fonts
    */
   private Vector<PDFFont> fonts;

   /**
    * <p>
    * This is used to provide a unique name for an image
    * </p>
    */
   private int imageid = 0;

   /**
    * This is the info object. Although this is an optional object, we include
    * it.
    */
   private PDFInfo info;

   /**
    * This vector contains each indirect object within the document.
    */
   protected Vector<PDFObject> objects;

   /**
    * This is used to allocate objects a unique serial number in the document.
    */
   protected int objser;

   /**
    * This is the Outline object, which is optional
    */
   private PDFOutline outline;

   /**
    * This is the Pages object, which is required by each PDF Document
    */
   private PDFPageList pdfPageList;

   /**
    * <p>
    * This creates a PDF document with the default pagemode
    * </p>
    */
   public PDFDocument()
   {
      this(USENONE);
   }

   /**
    * <p>
    * This creates a PDF document
    * </p>
    * 
    * @param pagemode
    *            an int, determines how the document will present itself to the
    *            viewer when it first opens.
    */
   public PDFDocument(int pagemode)
   {
      objser = 1;
      objects = new Vector<PDFObject>();
      fonts = new Vector<PDFFont>();

      // Now create some standard objects
      add(pdfPageList = new PDFPageList());
      add(catalog = new PDFCatalog(pdfPageList, pagemode));
      add(info = new PDFInfo());

      // Acroread on linux seems to die if there is no root outline
      add(getOutline());
   }

   /**
    * This adds a top level object to the document.
    * <p>
    * Once added, it is allocated a unique serial number.
    * <p>
    * <b>Note:</b> Not all object are added directly using this method. Some
    * objects which have Kids (in PDF sub-objects or children are called Kids)
    * will have their own add() method, which will call this one internally.
    * 
    * @param obj
    *            The PDFObject to add to the document
    * @return the unique serial number for this object.
    */
   public synchronized int add(PDFObject obj)
   {
      objects.addElement(obj);
      obj.objser = objser++; // create a new serial number
      obj.pdfDocument = this; // so they can find the document they belong to

      // If its a page, then add it to the pages collection
      if (obj instanceof PDFPage)
         pdfPageList.add((PDFPage) obj);

      return obj.objser;
   }

   /**
    * This returns a font of the specified type and font. If the font has not
    * been defined, it creates a new font in the PDF document, and returns it.
    * 
    * @param type
    *            PDF Font Type - usually "/Type1"
    * @param font
    *            Java font name
    * @param style
    *            java.awt.Font style (NORMAL, BOLD etc)
    * @return PDFFont defining this font
    */
   public PDFFont getFont(String type, String font, int style)
   {
      for (PDFFont ft : fonts)
         if (ft.equals(type, font, style))
            return ft;

            // the font wasn't found, so create it
            fontid++;
            PDFFont ft = new PDFFont("/F" + fontid, type, font, style);
            add(ft);
            fonts.addElement(ft);
            return ft;
   }

   /**
    * @return the root outline
    */
   public PDFOutline getOutline()
   {
      if (outline == null)
      {
         outline = new PDFOutline();
         catalog.setOutline(outline);
      }
      return outline;
   }

   /**
    * <p>
    * This returns a specific page. It's used mainly when using a Serialized
    * template file.
    * </p>
    * ?? How does a serialized template file work ???
    * 
    * @param page
    *            page number to return
    * @return PDFPage at that position
    */
   public PDFPage getPage(int page)
   {
      return pdfPageList.getPage(page);
   }

   /**
    * <p>
    * Get the PDFInfo object, which contains author, title, keywords, etc
    * </p>
    * 
    * @return the PDFInfo object for this document.
    */
   public PDFInfo getPDFInfo()
   {
      return this.info;
   }

   /**
    * Sets a unique name to a PDFImage
    * 
    * @param img
    *            PDFImage to set the name of
    * @return the name given to the image
    */
   public String setImageName(PDFImage img)
   {
      imageid++;
      img.setName("/Image" + imageid);
      return img.getName();
   }

   /**
    * <p>
    * Set the PDFInfo object, which contains author, title, keywords, etc
    * </p>
    * 
    * @param info
    *            a PDFInof object
    */
   public void setPDFInfo(PDFInfo info)
   {
      this.info = info;
   }

   /**
    * This writes the document to an OutputStream.
    * <p>
    * <b>Note:</b> You can call this as many times as you wish, as long as the
    * calls are not running at the same time.
    * <p>
    * Also, objects can be added or amended between these calls.
    * <p>
    * Also, the OutputStream is not closed, but will be flushed on completion.
    * It is up to the caller to close the stream.
    * 
    * @param os
    *            OutputStream to write the document to
    * @exception IOException
    *                on error
    */
   public void write(OutputStream os) throws IOException
   {
      PDFOutput pos = new PDFOutput(os);

      // Write each object to the OutputStream. We call via the output
      // as that builds the xref table
      for (PDFObject o : objects)
         pos.write(o);

            // Finally close the output, which writes the xref table.
            pos.close();

            // and flush the output stream to ensure everything is written.
            os.flush();
   }

} // end class PDFDocument
